import sys
import cv2
import numpy as np
import json
import os
import datetime
import time
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QLabel, QFrame, QRadioButton,
                             QPushButton, QVBoxLayout, QHBoxLayout, QGroupBox,
                             QStatusBar, QLineEdit, QFormLayout, QSlider, QTabWidget,
                             QCheckBox, QGridLayout, QTextEdit, QFileDialog, QButtonGroup, QComboBox)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QMutex, QMutexLocker
from PyQt5.QtGui import QImage, QPixmap, QColor, QPalette, QFont, QIntValidator
import mediapipe as mp
import pygame
from pygame import mixer
import socket

# 添加模型路径设置
if getattr(sys, 'frozen', False):
    base_path = sys._MEIPASS
else:
    base_path = os.path.dirname(os.path.abspath(__file__))

model_path = os.path.join(base_path, 'mediapipe_models')
os.environ['MEDIAPIPE_MODEL_BASE_DIR'] = model_path

# 初始化MediaPipe
mp_hands = mp.solutions.hands
mp_pose = mp.solutions.pose
mp_drawing = mp.solutions.drawing_utils

class UDPReceiverThread(QThread):
    """UDP视频流接收线程"""
    frame_signal = pyqtSignal(np.ndarray)
    status_signal = pyqtSignal(str)
    
    def __init__(self, port=12345):
        super().__init__()
        self.port = port
        self.running = False
        self.sock = None
        
    def run(self):
        self.running = True
        self.status_signal.emit(f"开始监听UDP端口 {self.port}...")
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.sock.bind(('0.0.0.0', self.port))
            self.sock.settimeout(0.1)  # 设置超时以便检查运行状态
            
            buffer = b""
            while self.running:
                try:
                    data, addr = self.sock.recvfrom(65507)  # 最大UDP包大小
                    buffer += data
                    
                    # 查找JPEG起始标记
                    start_pos = buffer.find(b'\xff\xd8')
                    if start_pos == -1:
                        continue
                    
                    # 查找JPEG结束标记
                    end_pos = buffer.find(b'\xff\xd9', start_pos)
                    if end_pos == -1:
                        continue
                    
                    # 提取完整JPEG帧
                    jpeg_data = buffer[start_pos:end_pos+2]
                    buffer = buffer[end_pos+2:]
                    
                    # 解码JPEG图像
                    frame = cv2.imdecode(np.frombuffer(jpeg_data, dtype=np.uint8), cv2.IMREAD_COLOR)
                    if frame is not None:
                        self.frame_signal.emit(frame)
                    
                except socket.timeout:
                    continue
                except Exception as e:
                    self.status_signal.emit(f"接收错误: {str(e)}")
                    time.sleep(0.1)
                    
        except Exception as e:
            self.status_signal.emit(f"UDP接收失败: {str(e)}")
        finally:
            if self.sock:
                self.sock.close()
            self.status_signal.emit("UDP接收已停止")
    
    def stop(self):
        self.running = False
        self.wait(1000)

class VideoProcessorThread(QThread):
    """视频处理线程 - 从UDP接收帧"""
    frame_signal = pyqtSignal(np.ndarray)
    status_signal = pyqtSignal(str)
    
    def __init__(self, udp_receiver):
        super().__init__()
        self.udp_receiver = udp_receiver
        self.running = False
    
    def run(self):
        self.running = True
        self.status_signal.emit("视频处理已启动")
        
        while self.running:
            time.sleep(0.01)  # 保持低CPU占用
    
    def stop(self):
        self.running = False
        self.wait(1000)

class ImageLabel(QLabel):
    """支持鼠标点击获取坐标的自定义标签"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAlignment(Qt.AlignCenter)
        self.setMouseTracking(True)

class DetectionThread(QThread):
    """AI检测处理线程 - 优化版本"""
    result_signal = pyqtSignal(np.ndarray, np.ndarray, bool, bool, dict, dict, int, bool, float)
    debug_signal = pyqtSignal(str)  # 新增调试信号
    
    def __init__(self, detector):
        super().__init__()
        self.detector = detector
        self.running = False
        self.mutex = QMutex()
        self.latest_frame = None
        self.has_new_frame = False
    
    def add_frame(self, frame):
        """更新最新帧"""
        with QMutexLocker(self.mutex):
            self.latest_frame = frame
            self.has_new_frame = True
    
    def run(self):
        self.running = True
        while self.running:
            frame = None
            # 获取最新帧
            with QMutexLocker(self.mutex):
                if self.has_new_frame:
                    frame = self.latest_frame
                    self.has_new_frame = False
            
            if frame is None:
                self.msleep(100)  # 增加休眠时间减少CPU占用
                continue
                
            try:
                # 处理帧
                raw_frame, processed_frame, left_detected, right_detected, left_info, right_info, person_detected, person_confidence = self.detector.process_frame(frame)
                self.result_signal.emit(
                    raw_frame, processed_frame, left_detected, right_detected, 
                    left_info, right_info, self.detector.missed_detections, 
                    person_detected, person_confidence
                )
            except Exception as e:
                print(f"检测处理错误: {str(e)}")
                self.debug_signal.emit(f"检测处理错误: {str(e)}")
    
    def stop(self):
        self.running = False
        self.wait(1000)

class GloveDetector:
    def __init__(self):
        # 默认参数
        self.params = {
            "global_point_interval": 5,
            "detection_interval": 500,
            "hand_confidence_threshold": 0.5,
            "h_min": 0,
            "h_max": 180,
            "s_min": 0,
            "s_max": 255,
            "v_min": 0,
            "v_max": 255,
            "color_tolerance": 30,
            "point_threshold": 20,
            "ratio_threshold": 0.5,
            "max_missed_detections": 5,
            "pause_recognition": False,
            "wrist_radius": 30
        }
        
        self.debug_mode = False
        self.force_detection = False
        self.force_show_points = False
        
        self.left_glove_detected = False
        self.right_glove_detected = False
        self.missed_detections = 0
        self.last_detection_time = 0
        self.no_person_count = 0  # 初始化无人计数
        self.person_detected = False  # 初始化人员检测状态
        self.person_confidence = 0.0  # 人员检测置信度
        
        self.original_frame = None
        self.processed_frame = None
        self.overlay_frame = None
        
        self.hands = None
        self.pose = None
        
        # 加载参数
        self.params_file = "glove_params.json"
        self.load_parameters()
        
        # 应用参数
        self.__dict__.update(self.params)
        
        # 初始化模型
        self.initialize_models()
        
        # 检测帧率控制
        self.last_detection_time = 0
        
        # 手动录制状态
        self.manual_recording = False
        self.manual_video_writer = None
        
        # 报警状态
        self.alarm_active = False
        self.alarm_start_time = None
        self.alarm_duration = 1.0
        
        # 优化: 预分配HSV掩码
        self.hsv_mask = None

        # 新增报警音频播放状态
        self.alarm_sound_playing = False
        
        # 调试信息
        self.debug_info = ""
    
    def initialize_models(self):
        """初始化MediaPipe模型"""
        try:
            # 手部模型
            self.hands = mp_hands.Hands(
                static_image_mode=False,
                max_num_hands=2,
                min_detection_confidence=0.6,
                min_tracking_confidence=0.5,
                model_complexity=1)
            
            # 姿势模型
            self.pose = mp_pose.Pose(
                static_image_mode=False,
                min_detection_confidence=0.6,
                model_complexity=1)
        except Exception as e:
            print(f"模型初始化错误: {str(e)}")
    
    def load_parameters(self):
        try:
            if os.path.exists(self.params_file):
                with open(self.params_file, 'r') as f:
                    self.params.update(json.load(f))
        except Exception as e:
            print(f"加载参数错误: {str(e)}")
    
    def save_parameters(self):
        try:
            with open(self.params_file, 'w') as f:
                json.dump(self.params, f, indent=4)
            return True
        except Exception as e:
            print(f"保存参数错误: {str(e)}")
            return False
    
    def process_frame(self, frame):
        if frame is None or frame.size == 0:
            return None, None, False, False, {}, {}, False, 0.0
            
        # 重置调试信息
        self.debug_info = ""
            
        # 创建用于显示的原始帧副本
        if self.debug_mode or self.force_show_points:
            display_frame = frame.copy()
            self.draw_global_glove_points(display_frame)
            self.force_show_points = False
        else:
            display_frame = frame
            
        if self.pause_recognition and not self.force_detection:
            return display_frame, frame, self.left_glove_detected, self.right_glove_detected, {}, {}, self.person_detected, self.person_confidence
            
        current_time = time.time() * 1000
        
        # 修改报警逻辑：只有检测到人在画面中时才报警
        if not self.left_glove_detected or not self.right_glove_detected:
            # 只有当人在画面中时才增加未检测计数
            if self.person_detected:
                self.missed_detections += 1
                
                if self.missed_detections >= self.max_missed_detections and not self.alarm_active:
                    self.alarm_active = True
                    self.alarm_start_time = datetime.datetime.now()
                    self.save_alarm_image(frame)
                    # 播放报警声音
                    self.play_alarm_sound()
                # 重置不在画面计数器
                self.no_person_count = 0
            else:
                # 如果人不在画面中，增加不在画面计数器
                self.no_person_count += 1
                
                # 当连续不在画面次数达到阈值时，解除报警
                if self.no_person_count >= self.max_missed_detections:
                    self.missed_detections = 0
                    if self.alarm_active:
                        self.alarm_active = False
                        self.stop_alarm_sound()
        else:
            self.missed_detections = 0
            self.no_person_count = 0
            if self.alarm_active:
                self.alarm_active = False
                self.stop_alarm_sound()

        # 仅在需要时进行检测
        if self.force_detection or self.last_detection_time == 0 or current_time - self.last_detection_time >= self.detection_interval:
            self.last_detection_time = current_time
            self.force_detection = False
            
            # 使用原始帧避免复制
            self.original_frame = frame
            self.processed_frame = frame
            self.overlay_frame = frame.copy()
            
            self.detect_hands_and_pose()
            
            left_info = {}
            right_info = {}
            
            # 混合模型检测逻辑
            hands_left, hands_right, hands_left_info, hands_right_info = self.draw_hands_model()
            
            # 如果手部模型检测到两只手，直接使用结果
            if hands_left is not None and hands_right is not None:
                self.left_glove_detected, self.right_glove_detected = hands_left, hands_right
                left_info, right_info = hands_left_info, hands_right_info
            # 如果只检测到一只手或没有手，切换到姿势模型
            else:
                pose_left, pose_right, pose_left_info, pose_right_info = self.draw_pose_model()
                # 修复问题3：确保检测结果正确传递
                if hands_left is not None:
                    self.left_glove_detected = hands_left
                elif pose_left is not None:
                    self.left_glove_detected = pose_left
                
                if hands_right is not None:
                    self.right_glove_detected = hands_right
                elif pose_right is not None:
                    self.right_glove_detected = pose_right
                
                # 确保信息正确传递
                if hands_left_info:
                    left_info = hands_left_info
                elif pose_left_info:
                    left_info = pose_left_info
                    
                if hands_right_info:
                    right_info = hands_right_info
                elif pose_right_info:
                    right_info = pose_right_info
            
            # 检测未佩戴手套的情况 - 现在在顶部统一处理
            return display_frame, self.overlay_frame, self.left_glove_detected, self.right_glove_detected, left_info, right_info, self.person_detected, self.person_confidence
        else:
            processed_frame = self.overlay_frame if self.overlay_frame is not None else frame
            return display_frame, processed_frame, self.left_glove_detected, self.right_glove_detected, {}, {}, self.person_detected, self.person_confidence

    def play_alarm_sound(self):
        """播放报警声音"""
        if not self.alarm_sound_playing:
            try:
                # 加载并播放报警声音
                mixer.music.load("alarm_sound.wav")  # 确保有alarm_sound.wav文件
                mixer.music.play(-1)  # -1表示循环播放
                self.alarm_sound_playing = True
            except Exception as e:
                print(f"播放报警声音失败: {str(e)}")
    
    def stop_alarm_sound(self):
        """停止报警声音"""
        if self.alarm_sound_playing:
            try:
                mixer.music.stop()
                self.alarm_sound_playing = False
            except Exception as e:
                print(f"停止报警声音失败: {str(e)}")
                
    def save_alarm_image(self, frame):
        """保存报警时的原始帧图片"""
        try:
            # 创建alarm_images目录
            if not os.path.exists("alarm_images"):
                os.makedirs("alarm_images")
                
            timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = os.path.join("alarm_images", f"glove_alarm_{timestamp}.jpg")
            cv2.imwrite(filename, frame)
            print(f"报警图片已保存: {filename}")
        except Exception as e:
            print(f"保存报警图片失败: {str(e)}")

    def get_hsv_bounds(self):
        """计算HSV边界范围，避免重复代码"""
        return (
            np.array([
                max(0, self.h_min - self.color_tolerance),
                max(0, self.s_min - self.color_tolerance),
                max(0, self.v_min - self.color_tolerance)
            ]),
            np.array([
                min(180, self.h_max + self.color_tolerance),
                min(255, self.s_max + self.color_tolerance),
                min(255, self.v_max + self.color_tolerance)
            ])
        )
    
    def draw_global_glove_points(self, frame):
        """在整个帧上绘制符合HSV阈值的点"""
        hsv_image = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        lower_bound, upper_bound = self.get_hsv_bounds()
        
        # 创建掩码
        hsv_mask = cv2.inRange(hsv_image, lower_bound, upper_bound)
        
        # 在帧上标记白手套点
        y_indices, x_indices = np.where(hsv_mask > 0)
        for i in range(0, len(x_indices), self.global_point_interval):
            x = x_indices[i]
            y = y_indices[i]
            cv2.circle(frame, (x, y), 1, (0, 255, 0), -1)
    
    def detect_hands_and_pose(self):
        if self.original_frame is None or self.original_frame.size == 0:
            self.person_detected = False
            self.person_confidence = 0.0
            return
            
        # 缩小图像以提高处理速度
        small_frame = cv2.resize(self.original_frame, (0, 0), fx=0.25, fy=0.25)
        rgb_frame = cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB)
        
        # 同时使用手部和姿态检测判断人员存在
        hands_results = self.hands.process(rgb_frame)
        pose_results = self.pose.process(rgb_frame)
        
        # 计算综合置信度
        hand_confidence = 0.0
        pose_confidence = 0.0
        
        # 手部检测置信度
        if hands_results.multi_hand_landmarks:
            for hand_landmarks in hands_results.multi_hand_landmarks:
                # 取所有关键点可见度的平均值
                visibilities = [landmark.visibility for landmark in hand_landmarks.landmark]
                hand_confidence = max(hand_confidence, sum(visibilities) / len(visibilities))
        
        # 姿态检测置信度
        if pose_results.pose_landmarks:
            # 取上半身关键点（肩膀、肘部、手腕）的可见度平均值
            upper_body_indices = [
                mp_pose.PoseLandmark.LEFT_SHOULDER,
                mp_pose.PoseLandmark.RIGHT_SHOULDER,
                mp_pose.PoseLandmark.LEFT_ELBOW,
                mp_pose.PoseLandmark.RIGHT_ELBOW,
                mp_pose.PoseLandmark.LEFT_WRIST,
                mp_pose.PoseLandmark.RIGHT_WRIST
            ]
            
            visibilities = []
            for landmark_idx in upper_body_indices:
                landmark = pose_results.pose_landmarks.landmark[landmark_idx.value]
                visibilities.append(landmark.visibility)
            
            if visibilities:
                pose_confidence = sum(visibilities) / len(visibilities)
        
        # 综合置信度取两者最大值
        self.person_confidence = max(hand_confidence, pose_confidence)
        
        # 检测是否有人（通过手部或姿态检测）
        self.person_detected = (
            (hands_results is not None and hands_results.multi_hand_landmarks is not None) or
            (pose_results is not None and pose_results.pose_landmarks is not None)
        )
        
        # 保存结果用于后续处理
        self.hands_results = hands_results
        self.pose_results = pose_results
    
    def draw_hands_model(self):
        left_glove_detected = None
        right_glove_detected = None
        left_info = {}
        right_info = {}
        
        if self.hands_results is None or self.hands_results.multi_hand_landmarks is None:
            return left_glove_detected, right_glove_detected, left_info, right_info
        
        h, w, _ = self.original_frame.shape
        
        for hand_landmarks, handedness in zip(self.hands_results.multi_hand_landmarks, self.hands_results.multi_handedness):
            mp_drawing.draw_landmarks(
                self.overlay_frame,
                hand_landmarks,
                mp_hands.HAND_CONNECTIONS,
                mp_drawing.DrawingSpec(color=(255, 0, 0)), 
                mp_drawing.DrawingSpec(color=(0, 0, 255)))
            
            hand_type = handedness.classification[0].label
            mask = np.zeros((h, w), dtype=np.uint8)
            points = []
            
            for landmark in hand_landmarks.landmark:
                visibility = 1.0 - (landmark.z if landmark.z > 0 else 0)
                if visibility > self.hand_confidence_threshold:
                    # 坐标需要放大2倍，因为我们缩小了图像
                    x = min(max(int(landmark.x * w), 0), w-1)
                    y = min(max(int(landmark.y * h), 0), h-1)
                    points.append([x, y])
            
            if len(points) < 5:
                continue
            
            points = np.array(points)
            hull = cv2.convexHull(points)
            cv2.fillConvexPoly(mask, hull, 255)
            
            glove_detected, glove_points, normalized_points = self.detect_glove_in_region(mask, hand_type, "hands")
            
            if hand_type == "Left":
                left_glove_detected = glove_detected
                left_info = {
                    'points': glove_points,
                    'ratio': normalized_points,
                    'method': 'hands'
                }
            else:
                right_glove_detected = glove_detected
                right_info = {
                    'points': glove_points,
                    'ratio': normalized_points,
                    'method': 'hands'
                }
                
        return left_glove_detected, right_glove_detected, left_info, right_info

    def draw_pose_model(self):
        left_glove_detected = None
        right_glove_detected = None
        left_info = {}
        right_info = {}
        
        if self.pose_results is None or self.pose_results.pose_landmarks is None:
            return left_glove_detected, right_glove_detected, left_info, right_info
        
        h, w, _ = self.original_frame.shape
        
        mp_drawing.draw_landmarks(
            self.overlay_frame,
            self.pose_results.pose_landmarks,
            mp_pose.POSE_CONNECTIONS,
            landmark_drawing_spec=mp_drawing.DrawingSpec(color=(0, 255, 0)), 
            connection_drawing_spec=mp_drawing.DrawingSpec(color=(0, 255, 255)))
        
        # 获取左右手腕位置
        left_wrist_idx = mp_pose.PoseLandmark.LEFT_WRIST.value
        right_wrist_idx = mp_pose.PoseLandmark.RIGHT_WRIST.value
        
        # 检测左手
        if self.pose_results.pose_landmarks.landmark[left_wrist_idx].visibility > self.hand_confidence_threshold:
            wrist_landmark = self.pose_results.pose_landmarks.landmark[left_wrist_idx]
            wrist_x = int(wrist_landmark.x * w)
            wrist_y = int(wrist_landmark.y * h)
            
            mask = np.zeros((h, w), dtype=np.uint8)
            cv2.circle(mask, (wrist_x, wrist_y), self.wrist_radius, 255, -1)
            
            glove_detected, glove_points, normalized_points = self.detect_glove_in_region(mask, "Left", "pose")
            color = (0, 255, 0) if glove_detected else (0, 0, 255)
            cv2.circle(self.overlay_frame, (wrist_x, wrist_y), 15, color, 3)
            
            left_glove_detected = glove_detected
            left_info = {
                'points': glove_points,
                'ratio': normalized_points,
                'method': 'pose'
            }
        
        # 检测右手
        if self.pose_results.pose_landmarks.landmark[right_wrist_idx].visibility > self.hand_confidence_threshold:
            wrist_landmark = self.pose_results.pose_landmarks.landmark[right_wrist_idx]
            wrist_x = int(wrist_landmark.x * w)
            wrist_y = int(wrist_landmark.y * h)
            
            mask = np.zeros((h, w), dtype=np.uint8)
            cv2.circle(mask, (wrist_x, wrist_y), self.wrist_radius, 255, -1)
            
            glove_detected, glove_points, normalized_points = self.detect_glove_in_region(mask, "Right", "pose")
            color = (0, 255, 0) if glove_detected else (0, 0, 255)
            cv2.circle(self.overlay_frame, (wrist_x, wrist_y), 15, color, 3)
            
            right_glove_detected = glove_detected
            right_info = {
                'points': glove_points,
                'ratio': normalized_points,
                'method': 'pose'
            }
                
        return left_glove_detected, right_glove_detected, left_info, right_info

    def detect_glove_in_region(self, mask, hand_type, method="hands"):
        # 获取区域内图像
        region_image = cv2.bitwise_and(self.original_frame, self.original_frame, mask=mask)
        hsv_image = cv2.cvtColor(region_image, cv2.COLOR_BGR2HSV)
        lower_bound, upper_bound = self.get_hsv_bounds()
        
        color_mask = cv2.inRange(hsv_image, lower_bound, upper_bound)
        glove_points_in_region = cv2.countNonZero(color_mask)
        
        region_area = cv2.countNonZero(mask)
        if region_area == 0:  # 避免除零错误
            return False, 0, 0.0
            
        normalized_points = glove_points_in_region / region_area
        
        # 判断是否戴手套
        glove_detected = (glove_points_in_region >= self.point_threshold and 
                         normalized_points >= self.ratio_threshold)
        
        # 绘制轮廓
        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        contour_color = (0, 255, 0) if glove_detected else (0, 0, 255)
        method_color = (200, 0, 255) if method == "hands" else (255, 200, 0)
        
        for contour in contours:
            if cv2.contourArea(contour) > 100:
                cv2.drawContours(self.overlay_frame, [contour], -1, contour_color, 10)
                cv2.drawContours(self.overlay_frame, [contour], -1, method_color, 3)
        
        # 在区域内标记白手套点
        y_indices, x_indices = np.where(color_mask > 0)
        for i in range(0, len(x_indices), 5):
            x = x_indices[i]
            y = y_indices[i]
            cv2.circle(self.overlay_frame, (x, y), 1, (0, 255, 0), -1)
        
        return glove_detected, glove_points_in_region, normalized_points
    
    def start_manual_recording(self):
        if not self.manual_recording:
            self.manual_recording = True
            self.manual_record_start_time = datetime.datetime.now()
            timestamp = self.manual_record_start_time.strftime("%Y%m%d_%H%M%S")
            
            # 创建recordings目录
            if not os.path.exists("recordings"):
                os.makedirs("recordings")
                
            filename = os.path.join("recordings", f"manual_record_{timestamp}.mp4")
            
            # 确保有帧可以获取尺寸
            if hasattr(self, 'original_frame') and self.original_frame is not None:
                h, w = self.original_frame.shape[:2]
            else:
                h, w = 720, 1280  # 默认尺寸
                
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            self.manual_video_writer = cv2.VideoWriter(filename, fourcc, 15.0, (w, h))
            if not self.manual_video_writer.isOpened():
                print(f"无法打开视频文件: {filename}")
                self.manual_video_writer = None
                self.manual_recording = False
                return False
            return True
        return False
        
    def stop_manual_recording(self):
        if self.manual_recording:
            self.manual_recording = False
            if self.manual_video_writer:
                self.manual_video_writer.release()
                self.manual_video_writer = None
                return True
        return False
    
    def record_frame(self, frame):
        if frame is None or frame.size == 0:
            return
            
        if self.manual_recording and self.manual_video_writer:
            self.manual_video_writer.write(frame)

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("ESP32 CAM 白手套检测系统 V1.0.0.0")
        self.setGeometry(100, 100, 1400, 800)
        
        self.video_thread = None
        self.detection_thread = None
        self.glove_detector = GloveDetector()
        self.current_frame = None
        self.paused_frame = None
        self.debug_mode = False
        self.video_paused = False
        
        # UDP接收器
        self.udp_receiver = UDPReceiverThread(port=12345)
        self.udp_video_processor = None
        
        self.alarm_timer = QTimer()
        self.alarm_timer.timeout.connect(self.update_alarm)
        self.alarm_timer.start(200)
        self.max_alarm_duration = 30  # 最大报警持续时间（秒）

        self.init_ui()
        self.set_dark_theme()
    
    def init_ui(self):
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        main_layout = QHBoxLayout(central_widget)
        
        # 左侧视频区域
        self.video_frame = QFrame()
        self.video_frame.setFrameShape(QFrame.StyledPanel)
        video_layout = QVBoxLayout(self.video_frame)
        
        video_display_layout = QHBoxLayout()
        self.raw_video_label = ImageLabel()
        self.raw_video_label.setObjectName("raw_video_label")
        self.raw_video_label.setAlignment(Qt.AlignCenter)
        self.raw_video_label.setMinimumSize(300, 200)
        self.raw_video_label.setStyleSheet("background-color: #1e1e1e; border: 1px solid #444;")
        video_display_layout.addWidget(self.raw_video_label, 40)

        # 右侧面板
        right_panel = QVBoxLayout()
        self.last_detection_label = QLabel("检测结果")
        self.last_detection_label.setAlignment(Qt.AlignCenter)
        self.last_detection_label.setStyleSheet("font-size: 14px; color: #aaa; margin-bottom: 5px;")

        self.last_detection_frame = QLabel()
        self.last_detection_frame.setAlignment(Qt.AlignCenter)
        self.last_detection_frame.setMinimumSize(600, 400)
        self.last_detection_frame.setStyleSheet("background-color: #1e1e1e; border: 1px solid #444;")
        self.last_detection_frame.setScaledContents(True)

        # 手套检测状态
        self.glove_status_frame = QFrame()
        glove_status_layout = QGridLayout(self.glove_status_frame)
        
        self.left_hand_label = QLabel("左手状态:")
        self.left_hand_label.setStyleSheet("font-size: 14px;")
        glove_status_layout.addWidget(self.left_hand_label, 0, 0)
        
        self.left_hand_result = QLabel("检测中...")
        self.left_hand_result.setStyleSheet("font-size: 16px; font-weight: bold;")
        glove_status_layout.addWidget(self.left_hand_result, 0, 1)
        
        self.left_hand_info = QLabel("")
        self.left_hand_info.setStyleSheet("font-size: 12px; color: #aaa;")
        glove_status_layout.addWidget(self.left_hand_info, 0, 2, 1, 2)  # 跨两列
        
        self.right_hand_label = QLabel("右手状态:")
        self.right_hand_label.setStyleSheet("font-size: 14px;")
        glove_status_layout.addWidget(self.right_hand_label, 1, 0)
        
        self.right_hand_result = QLabel("检测中...")
        self.right_hand_result.setStyleSheet("font-size: 16px; font-weight: bold;")
        glove_status_layout.addWidget(self.right_hand_result, 1, 1)
        
        self.right_hand_info = QLabel("")
        self.right_hand_info.setStyleSheet("font-size: 12px; color: #aaa;")
        glove_status_layout.addWidget(self.right_hand_info, 1, 2, 1, 2)  # 跨两列
        
        # 添加即将报警计数显示
        self.accumulated_label = QLabel("即将报警计数:")
        self.accumulated_label.setStyleSheet("font-size: 14px; color: #ffcc00;")
        glove_status_layout.addWidget(self.accumulated_label, 2, 0)

        self.accumulated_value = QLabel("0")
        self.accumulated_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #ffcc00;")
        glove_status_layout.addWidget(self.accumulated_value, 2, 1)

        # 添加报警状态显示
        self.alarm_status_label = QLabel("报警状态:")
        self.alarm_status_label.setStyleSheet("font-size: 14px; color: #ffcc00;")
        glove_status_layout.addWidget(self.alarm_status_label, 2, 2)
        
        self.alarm_status_value = QLabel("未激活")
        self.alarm_status_value.setStyleSheet("font-size: 16px; font-weight: bold;")
        glove_status_layout.addWidget(self.alarm_status_value, 2, 3)

        # 添加人员检测状态
        self.person_status_label = QLabel("人员检测:")
        self.person_status_label.setStyleSheet("font-size: 14px; color: #ffcc00;")
        glove_status_layout.addWidget(self.person_status_label, 3, 0)
        
        self.person_status_value = QLabel("未检测")
        self.person_status_value.setStyleSheet("font-size: 16px; font-weight: bold;")
        glove_status_layout.addWidget(self.person_status_value, 3, 1)
        
        # 添加无人计数状态
        self.no_person_count_label = QLabel("无人计数:")
        self.no_person_count_label.setStyleSheet("font-size: 14px; color: #ffcc00;")
        glove_status_layout.addWidget(self.no_person_count_label, 3, 2)
        
        self.no_person_count_value = QLabel("0")
        self.no_person_count_value.setStyleSheet("font-size: 16px; font-weight: bold;")
        glove_status_layout.addWidget(self.no_person_count_value, 3, 3)
        
        # 添加人员检测置信度
        self.person_confidence_label = QLabel("人员置信度:")
        self.person_confidence_label.setStyleSheet("font-size: 14px; color: #ffcc00;")
        glove_status_layout.addWidget(self.person_confidence_label, 4, 0)
        
        self.person_confidence_value = QLabel("0.00")
        self.person_confidence_value.setStyleSheet("font-size: 16px; font-weight: bold;")
        glove_status_layout.addWidget(self.person_confidence_value, 4, 1)

        right_panel.addWidget(self.last_detection_label)
        right_panel.addWidget(self.last_detection_frame)
        right_panel.addWidget(self.glove_status_frame)
        video_display_layout.addLayout(right_panel, 60)
        video_layout.addLayout(video_display_layout)

        # 状态信息
        self.status_label = QLabel("状态: 未连接")
        self.status_label.setFont(QFont("Arial", 10))
        self.status_label.setStyleSheet("color: #aaa;")
        video_layout.addWidget(self.status_label)

        # 消息框
        self.log_group = QGroupBox("系统消息")
        log_layout = QVBoxLayout()
        self.message_box = QTextEdit()
        self.message_box.setReadOnly(True)
        self.message_box.setMinimumHeight(100)
        self.message_box.setStyleSheet("background-color: #f9f9f9; font-size: 12px;")
        log_layout.addWidget(self.message_box)
        self.log_group.setLayout(log_layout)
        video_layout.addWidget(self.log_group)
        
        main_layout.addWidget(self.video_frame, 70)
        
        # 右侧控制区域
        self.control_frame = QFrame()
        self.control_layout = QVBoxLayout(self.control_frame)
        self.tab_widget = QTabWidget()
        self.control_layout.addWidget(self.tab_widget)
        
        # UDP设置标签页
        self.udp_tab = QWidget()
        self.init_udp_tab()
        self.tab_widget.addTab(self.udp_tab, "UDP设置")
        
        # 手套检测标签页
        self.glove_tab = QWidget()
        self.init_glove_tab()
        self.tab_widget.addTab(self.glove_tab, "手套检测设置")
        
        main_layout.addWidget(self.control_frame, 30)
        
        # 状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        self.status_bar.showMessage("就绪")
        self.status_bar.setStyleSheet("background-color: #252526; color: #d4d4d4;")
    
    def init_udp_tab(self):
        """UDP设置标签页"""
        layout = QVBoxLayout(self.udp_tab)
        layout.setSpacing(10)
        
        # UDP端口设置
        udp_group = QGroupBox("UDP设置")
        udp_layout = QFormLayout()
        self.udp_port_input = QLineEdit("12345")
        self.udp_port_input.setValidator(QIntValidator(1, 65535))
        udp_layout.addRow("UDP端口:", self.udp_port_input)
        udp_group.setLayout(udp_layout)
        layout.addWidget(udp_group)

        # 连接按钮
        self.connect_btn = QPushButton("启动UDP接收")
        self.connect_btn.setStyleSheet(self.get_button_style("#4CAF50", "#45a049"))
        self.connect_btn.clicked.connect(self.toggle_connection)
        layout.addWidget(self.connect_btn)

        # 手动录制按钮
        self.manual_record_btn = QPushButton("开始手动录制")
        self.manual_record_btn.setStyleSheet(self.get_button_style("#FF5722", "#E64A19"))
        self.manual_record_btn.clicked.connect(self.toggle_manual_recording)
        self.manual_record_btn.setEnabled(False)
        layout.addWidget(self.manual_record_btn)

        # 状态标签
        self.connection_label = QLabel("连接状态: 未连接")
        self.connection_label.setStyleSheet("color: #ff6b6b;")
        layout.addWidget(self.connection_label)
        
        self.stream_label = QLabel("流状态: 停止")
        self.stream_label.setStyleSheet("color: #ff6b6b;")
        layout.addWidget(self.stream_label)
        
        layout.addStretch(1)
    
    def toggle_connection(self):
        """切换UDP连接状态"""
        if hasattr(self, 'udp_receiver') and self.udp_receiver.isRunning():
            self.disconnect_udp()
        else:
            self.connect_udp()

    def connect_udp(self):
        """连接UDP视频流"""
        try:
            port = int(self.udp_port_input.text())
        except ValueError:
            self.status_bar.showMessage("无效的UDP端口号")
            return False
            
        self.status_bar.showMessage("正在启动UDP接收...")
        
        # 创建并启动UDP接收线程
        self.udp_receiver = UDPReceiverThread(port=port)
        self.udp_receiver.frame_signal.connect(self.update_frame)
        self.udp_receiver.status_signal.connect(self.update_status)
        self.udp_receiver.start()
        
        # 创建并启动视频处理线程
        self.udp_video_processor = VideoProcessorThread(self.udp_receiver)
        self.udp_video_processor.start()
        
        # 更新UI状态
        self.connect_btn.setText("停止接收")
        self.connect_btn.setStyleSheet(self.get_button_style("#f44336", "#d32f2f"))
        self.connection_label.setText("连接状态: UDP已连接")
        self.connection_label.setStyleSheet("color: #4caf50;")
        self.status_label.setText("状态: UDP接收中")
        self.stream_label.setText("流状态: 运行中")
        self.stream_label.setStyleSheet("color: #4caf50;")
        
        # 创建检测线程
        if not self.detection_thread or not self.detection_thread.isRunning():
            self.detection_thread = DetectionThread(self.glove_detector)
            self.detection_thread.result_signal.connect(
                lambda rf, pf, ld, rd, li, ri, mc, pd, pc: 
                self.update_detection_result(rf, pf, ld, rd, li, ri, mc, pd, pc))
            self.detection_thread.debug_signal.connect(self.add_message)  # 连接调试信号
            self.detection_thread.start()
            
        self.manual_record_btn.setEnabled(True)
        return True
    
    def disconnect_udp(self):
        """断开UDP连接"""
        if self.udp_receiver and self.udp_receiver.isRunning():
            self.udp_receiver.stop()
            self.udp_receiver.wait(1000)
        
        if hasattr(self, 'udp_video_processor') and self.udp_video_processor.isRunning():
            self.udp_video_processor.stop()
            self.udp_video_processor.wait(1000)
        
        # 更新UI状态
        self.connect_btn.setText("启动UDP接收")
        self.connect_btn.setStyleSheet(self.get_button_style("#4CAF50", "#45a049"))
        self.connection_label.setText("连接状态: 未连接")
        self.connection_label.setStyleSheet("color: #ff6b6b;")
        self.status_label.setText("状态: 未连接")
        self.stream_label.setText("流状态: 停止")
        self.stream_label.setStyleSheet("color: #ff6b6b;")
        self.manual_record_btn.setEnabled(False)
        
        # 停止检测线程
        if self.detection_thread and self.detection_thread.isRunning():
            self.detection_thread.stop()
            self.detection_thread = None

    def init_glove_tab(self):
        layout = QVBoxLayout(self.glove_tab)
        layout.setSpacing(10)

        # HSV颜色调节工具
        hsv_group = QGroupBox("HSV颜色调节")
        hsv_layout = QVBoxLayout()
        
        # H范围调节
        h_layout = QVBoxLayout()
        h_label = QLabel("H (色调) 范围:")
        h_layout.addWidget(h_label)
        
        h_min_layout = QHBoxLayout()
        h_min_label = QLabel("最小值:")
        self.h_min_slider = QSlider(Qt.Horizontal)
        self.h_min_slider.setRange(0, 180)
        self.h_min_slider.setValue(self.glove_detector.h_min)
        self.h_min_slider.valueChanged.connect(self.update_hsv_range)
        self.h_min_value = QLabel(str(self.glove_detector.h_min))
        h_min_layout.addWidget(h_min_label)
        h_min_layout.addWidget(self.h_min_slider)
        h_min_layout.addWidget(self.h_min_value)
        h_layout.addLayout(h_min_layout)
        
        h_max_layout = QHBoxLayout()
        h_max_label = QLabel("最大值:")
        self.h_max_slider = QSlider(Qt.Horizontal)
        self.h_max_slider.setRange(0, 180)
        self.h_max_slider.setValue(self.glove_detector.h_max)
        self.h_max_slider.valueChanged.connect(self.update_hsv_range)
        self.h_max_value = QLabel(str(self.glove_detector.h_max))
        h_max_layout.addWidget(h_max_label)
        h_max_layout.addWidget(self.h_max_slider)
        h_max_layout.addWidget(self.h_max_value)
        h_layout.addLayout(h_max_layout)
        
        hsv_layout.addLayout(h_layout)
        
        # S范围调节
        s_layout = QVBoxLayout()
        s_label = QLabel("S (饱和度) 范围:")
        s_layout.addWidget(s_label)
        
        s_min_layout = QHBoxLayout()
        s_min_label = QLabel("最小值:")
        self.s_min_slider = QSlider(Qt.Horizontal)
        self.s_min_slider.setRange(0, 255)
        self.s_min_slider.setValue(self.glove_detector.s_min)
        self.s_min_slider.valueChanged.connect(self.update_hsv_range)
        self.s_min_value = QLabel(str(self.glove_detector.s_min))
        s_min_layout.addWidget(s_min_label)
        s_min_layout.addWidget(self.s_min_slider)
        s_min_layout.addWidget(self.s_min_value)
        s_layout.addLayout(s_min_layout)
        
        s_max_layout = QHBoxLayout()
        s_max_label = QLabel("最大值:")
        self.s_max_slider = QSlider(Qt.Horizontal)
        self.s_max_slider.setRange(0, 255)
        self.s_max_slider.setValue(self.glove_detector.s_max)
        self.s_max_slider.valueChanged.connect(self.update_hsv_range)
        self.s_max_value = QLabel(str(self.glove_detector.s_max))
        s_max_layout.addWidget(s_max_label)
        s_max_layout.addWidget(self.s_max_slider)
        s_max_layout.addWidget(self.s_max_value)
        s_layout.addLayout(s_max_layout)
        
        hsv_layout.addLayout(s_layout)
        
        # V范围调节
        v_layout = QVBoxLayout()
        v_label = QLabel("V (亮度) 范围:")
        v_layout.addWidget(v_label)
        
        v_min_layout = QHBoxLayout()
        v_min_label = QLabel("最小值:")
        self.v_min_slider = QSlider(Qt.Horizontal)
        self.v_min_slider.setRange(0, 255)
        self.v_min_slider.setValue(self.glove_detector.v_min)
        self.v_min_slider.valueChanged.connect(self.update_hsv_range)
        self.v_min_value = QLabel(str(self.glove_detector.v_min))
        v_min_layout.addWidget(v_min_label)
        v_min_layout.addWidget(self.v_min_slider)
        v_min_layout.addWidget(self.v_min_value)
        v_layout.addLayout(v_min_layout)
        
        v_max_layout = QHBoxLayout()
        v_max_label = QLabel("最大值:")
        self.v_max_slider = QSlider(Qt.Horizontal)
        self.v_max_slider.setRange(0, 255)
        self.v_max_slider.setValue(self.glove_detector.v_max)
        self.v_max_slider.valueChanged.connect(self.update_hsv_range)
        self.v_max_value = QLabel(str(self.glove_detector.v_max))
        v_max_layout.addWidget(v_max_label)
        v_max_layout.addWidget(self.v_max_slider)
        v_max_layout.addWidget(self.v_max_value)
        v_layout.addLayout(v_max_layout)
        
        hsv_layout.addLayout(v_layout)
        
        # 颜色容差控制
        tolerance_layout = QHBoxLayout()
        tolerance_label = QLabel("颜色容差:")
        self.tolerance_slider = QSlider(Qt.Horizontal)
        self.tolerance_slider.setMinimum(10)
        self.tolerance_slider.setMaximum(60)
        self.tolerance_slider.setValue(self.glove_detector.color_tolerance)
        self.tolerance_slider.valueChanged.connect(self.update_color_tolerance)
        self.tolerance_value = QLabel(str(self.glove_detector.color_tolerance))
        tolerance_layout.addWidget(tolerance_label)
        tolerance_layout.addWidget(self.tolerance_slider)
        tolerance_layout.addWidget(self.tolerance_value)
        hsv_layout.addLayout(tolerance_layout)
        
        hsv_group.setLayout(hsv_layout)
        layout.addWidget(hsv_group)

        # 点采样参数
        point_group = QGroupBox("点采样参数")
        point_layout = QGridLayout()
        
        # 点数量阈值
        point_layout.addWidget(QLabel("点数量阈值:"), 0, 0)
        self.point_threshold_slider = QSlider(Qt.Horizontal)
        self.point_threshold_slider.setRange(1, 500)
        self.point_threshold_slider.setValue(self.glove_detector.point_threshold)
        self.point_threshold_slider.valueChanged.connect(self.update_point_threshold)
        point_layout.addWidget(self.point_threshold_slider, 0, 1)
        self.point_threshold_value = QLabel(str(self.glove_detector.point_threshold))
        point_layout.addWidget(self.point_threshold_value, 0, 2)
        
        # 比例阈值
        point_layout.addWidget(QLabel("比例阈值:"), 1, 0)
        self.ratio_threshold_slider = QSlider(Qt.Horizontal)
        self.ratio_threshold_slider.setRange(1, 100)
        self.ratio_threshold_slider.setValue(int(self.glove_detector.ratio_threshold * 100))
        self.ratio_threshold_slider.valueChanged.connect(self.update_ratio_threshold)
        point_layout.addWidget(self.ratio_threshold_slider, 1, 1)
        self.ratio_threshold_value = QLabel(f"{self.glove_detector.ratio_threshold:.2f}")
        point_layout.addWidget(self.ratio_threshold_value, 1, 2)
        point_layout.addWidget(QLabel("全局点采样间隔:"), 2, 0)

        self.global_point_interval_slider = QSlider(Qt.Horizontal)
        self.global_point_interval_slider.setRange(1, 20)
        self.global_point_interval_slider.setValue(self.glove_detector.global_point_interval)
        self.global_point_interval_slider.valueChanged.connect(self.update_global_point_interval)
        point_layout.addWidget(self.global_point_interval_slider, 2, 1)
        self.global_point_interval_value = QLabel(str(self.glove_detector.global_point_interval))
        point_layout.addWidget(self.global_point_interval_value, 2, 2)

        point_group.setLayout(point_layout)
        layout.addWidget(point_group)
        
        # 检测帧率控制
        frame_rate_group = QGroupBox("检测帧率")
        frame_rate_layout = QGridLayout()
        frame_rate_layout.addWidget(QLabel("检测间隔(毫秒):"), 0, 0)
        
        self.detection_interval_slider = QSlider(Qt.Horizontal)
        self.detection_interval_slider.setRange(100, 1000)
        self.detection_interval_slider.setValue(self.glove_detector.detection_interval)
        self.detection_interval_slider.valueChanged.connect(self.update_detection_interval)
        frame_rate_layout.addWidget(self.detection_interval_slider, 0, 1)
        
        self.detection_interval_value = QLabel(f"{self.glove_detector.detection_interval}ms")
        frame_rate_layout.addWidget(self.detection_interval_value, 0, 2)
        frame_rate_group.setLayout(frame_rate_layout)
        layout.addWidget(frame_rate_group)
        
        # 其他参数
        param_group = QGroupBox("检测参数")
        param_layout = QVBoxLayout()
        
        # 置信度阈值
        confidence_layout = QHBoxLayout()
        confidence_layout.addWidget(QLabel("手部置信度阈值:"))
        self.confidence_slider = QSlider(Qt.Horizontal)
        self.confidence_slider.setRange(10, 90)
        self.confidence_slider.setValue(int(self.glove_detector.hand_confidence_threshold * 100))
        self.confidence_slider.valueChanged.connect(self.update_confidence_threshold)
        confidence_layout.addWidget(self.confidence_slider)
        self.confidence_value = QLabel(f"{self.glove_detector.hand_confidence_threshold:.2f}")
        confidence_layout.addWidget(self.confidence_value)
        param_layout.addLayout(confidence_layout)

        # 手腕半径控制
        wrist_layout = QHBoxLayout()
        wrist_layout.addWidget(QLabel("手腕区域半径:"))
        self.wrist_radius_slider = QSlider(Qt.Horizontal)
        self.wrist_radius_slider.setRange(10, 100)
        self.wrist_radius_slider.setValue(self.glove_detector.wrist_radius)
        self.wrist_radius_slider.valueChanged.connect(self.update_wrist_radius)
        wrist_layout.addWidget(self.wrist_radius_slider)
        self.wrist_radius_value = QLabel(str(self.glove_detector.wrist_radius))
        wrist_layout.addWidget(self.wrist_radius_value)
        param_layout.addLayout(wrist_layout)
        
        param_group.setLayout(param_layout)
        layout.addWidget(param_group)

        # 报警设置
        alarm_group = QGroupBox("报警设置")
        alarm_layout = QVBoxLayout()
        self.alarm_checkbox = QCheckBox("启用未佩戴手套报警")
        self.alarm_checkbox.setChecked(True)
        alarm_layout.addWidget(self.alarm_checkbox)
        
        self.max_misses_label = QLabel("最大允许未检测次数:")
        self.max_misses_input = QLineEdit(str(self.glove_detector.max_missed_detections))
        self.max_misses_input.setValidator(QIntValidator(1, 100))
        alarm_layout.addWidget(self.max_misses_label)
        alarm_layout.addWidget(self.max_misses_input)
        
        alarm_group.setLayout(alarm_layout)
        layout.addWidget(alarm_group)
        
        # 暂停识别复选框
        self.pause_recognition_checkbox = QCheckBox("暂停手部识别")
        self.pause_recognition_checkbox.setChecked(self.glove_detector.pause_recognition)
        self.pause_recognition_checkbox.stateChanged.connect(self.toggle_pause_recognition)
        layout.addWidget(self.pause_recognition_checkbox)
        
        # 保存按钮
        self.save_params_button = QPushButton("保存参数")
        self.save_params_button.setMinimumHeight(30)
        self.save_params_button.clicked.connect(self.save_glove_parameters)
        layout.addWidget(self.save_params_button)
        
        layout.addStretch(1)
    
    def update_wrist_radius(self, value):
        """更新手腕半径设置"""
        self.glove_detector.wrist_radius = value
        self.wrist_radius_value.setText(str(value))
        self.add_message("手腕半径", f"手腕区域半径设置为: {value}像素")

    def set_dark_theme(self):
        dark_palette = QPalette()
        dark_palette.setColor(QPalette.Window, QColor(37, 37, 38))
        dark_palette.setColor(QPalette.WindowText, QColor(212, 212, 212))
        dark_palette.setColor(QPalette.Base, QColor(30, 30, 30))
        dark_palette.setColor(QPalette.ToolTipBase, QColor(37, 37, 38))
        dark_palette.setColor(QPalette.ToolTipText, QColor(212, 212, 212))
        dark_palette.setColor(QPalette.Text, QColor(212, 212, 212))
        dark_palette.setColor(QPalette.Button, QColor(45, 45, 48))
        dark_palette.setColor(QPalette.ButtonText, QColor(212, 212, 212))
        dark_palette.setColor(QPalette.Link, QColor(86, 156, 214))
        dark_palette.setColor(QPalette.Highlight, QColor(86, 156, 214))
        self.setPalette(dark_palette)
        
        self.setStyleSheet("""
            QGroupBox {
                border: 1px solid #444;
                border-radius: 5px;
                margin-top: 1ex;
                font-weight: bold;
                color: #d4d4d4;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 3px 0 3px;
            }
            QLabel {
                color: #d4d4d4;
            }
            QLineEdit {
                background-color: #252526;
                color: #d4d4d4;
                border: 1px solid #444;
                padding: 5px;
                border-radius: 3px;
            }
            QTabWidget::pane {
                border: 1px solid #444;
                top: 1px;
            }
            QTabBar::tab {
                background: #2d2d30;
                border: 1px solid #444;
                padding: 5px 10px;
            }
            QTabBar::tab:selected {
                background: #3e3e42;
                border-bottom: 2px solid #007acc;
            }
        """)
    
    def get_button_style(self, normal_color, hover_color):
        return f"""
            QPushButton {{
                background-color: {normal_color}; 
                color: white;
                padding: 8px;
                border-radius: 4px;
                font-weight: bold;
                min-width: 80px;
            }}
            QPushButton:hover {{
                background-color: {hover_color};
            }}
            QPushButton:disabled {{
                background-color: #555;
            }}
        """
    
    def update_frame(self, frame):
        """接收原始视频帧并发送到检测线程"""
        if frame is None or frame.size == 0:
            return
            
        # 保存当前帧
        self.current_frame = frame
        self.paused_frame = frame
        
        # 录制当前帧
        self.glove_detector.record_frame(frame)
        
        # 调试模式特殊处理
        if self.debug_mode:
            self.update_debug_display()
            return
            
        # 发送到检测线程
        if self.detection_thread and self.detection_thread.isRunning():
            self.detection_thread.add_frame(frame)
        
        # 显示原始帧
        self.display_frame(frame, self.raw_video_label)
    
    def display_frame(self, frame, label):
        """在指定标签上显示帧"""
        if frame is None or frame.size == 0:
            return
        # 优化：使用固定尺寸显示，减少计算量
        target_width = 640
        target_height = 360
        
        # 检查是否需要调整尺寸
        if frame.shape[1] != target_width or frame.shape[0] != target_height:
            resized_frame = cv2.resize(frame, (target_width, target_height))
        else:
            resized_frame = frame

        rgb_frame = cv2.cvtColor(resized_frame, cv2.COLOR_BGR2RGB)
        h, w, ch = rgb_frame.shape
        bytes_per_line = ch * w
        qt_image = QImage(rgb_frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
        
        if qt_image.isNull():
            return
            
        pixmap = QPixmap.fromImage(qt_image)
        scaled_pixmap = pixmap.scaled(
            label.size(), 
            Qt.KeepAspectRatio, 
            Qt.SmoothTransformation
        )
        label.setPixmap(scaled_pixmap)
    
    def update_detection_result(self, raw_frame, processed_frame, left_detected, right_detected, left_info, right_info, missed_count, person_detected, person_confidence):
        """接收检测线程的处理结果并更新UI"""
        # 在调试模式下，不更新检测结果
        if self.glove_detector.debug_mode:
            return
            
        # 更新检测帧显示
        if processed_frame is not None:
            self.display_frame(processed_frame, self.last_detection_frame)
        
        # 更新手套状态
        left_status = "已佩戴" if left_detected else "未佩戴"
        left_color = "#ccffcc" if left_detected else "#ffcccc"
        self.left_hand_result.setText(f"左手: {left_status}")
        self.left_hand_result.setStyleSheet(f"font-size: 16px; font-weight: bold; background-color: {left_color};")
        
        # 更新左手详细信息 - 添加阈值对比信息
        if left_info:
            method = left_info.get('method', '未知')
            points = left_info.get('points', 0)
            ratio = left_info.get('ratio', 0.0)
            # 添加阈值对比信息
            threshold_info = f"点阈值: {self.glove_detector.point_threshold}/{points}"
            ratio_info = f"占比阈值: {self.glove_detector.ratio_threshold:.2f}/{ratio:.2f}"
            self.left_hand_info.setText(f"{method} | {threshold_info} | {ratio_info}")
        else:
            self.left_hand_info.setText("无信息")
        
        right_status = "已佩戴" if right_detected else "未佩戴"
        right_color = "#ccffcc" if right_detected else "#ffcccc"
        self.right_hand_result.setText(f"右手: {right_status}")
        self.right_hand_result.setStyleSheet(f"font-size: 16px; font-weight: bold; background-color: {right_color};")
        
        # 更新右手详细信息 - 添加阈值对比信息
        if right_info:
            method = right_info.get('method', '未知')
            points = right_info.get('points', 0)
            ratio = right_info.get('ratio', 0.0)
            # 添加阈值对比信息
            threshold_info = f"点阈值: {self.glove_detector.point_threshold}/{points}"
            ratio_info = f"占比阈值: {self.glove_detector.ratio_threshold:.2f}/{ratio:.2f}"
            self.right_hand_info.setText(f"{method} | {threshold_info} | {ratio_info}")
        else:
            self.right_hand_info.setText("无信息")
            
        # 更新累加数据
        self.accumulated_value.setText(str(missed_count))
        
        # 根据计数设置不同颜色
        if missed_count >= self.glove_detector.max_missed_detections - 1:  # 接近阈值
            self.accumulated_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #ff0000;")  # 红色
        elif missed_count > 0:
            self.accumulated_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #ffcc00;")  # 黄色
        else:
            self.accumulated_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #00ff00;")  # 绿色
        
        # 更新报警状态显示
        alarm_status = "激活" if self.glove_detector.alarm_active else "未激活"
        alarm_color = "#ff0000" if self.glove_detector.alarm_active else "#00ff00"
        self.alarm_status_value.setText(alarm_status)
        self.alarm_status_value.setStyleSheet(f"font-size: 16px; font-weight: bold; color: {alarm_color};")
        
        # 更新人员检测状态
        person_status = "检测到人" if person_detected else "未检测到人"
        person_color = "#00ff00" if person_detected else "#ff0000"
        self.person_status_value.setText(person_status)
        self.person_status_value.setStyleSheet(f"font-size: 16px; font-weight: bold; color: {person_color};")
        
        # 更新无人计数
        self.no_person_count_value.setText(str(self.glove_detector.no_person_count))
        if self.glove_detector.no_person_count > 0:
            self.no_person_count_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #ffcc00;")
        else:
            self.no_person_count_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #00ff00;")
            
        # 更新人员检测置信度
        self.person_confidence_value.setText(f"{person_confidence:.2f}")
        if person_confidence > 0.5:
            self.person_confidence_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #00ff00;")
        elif person_confidence > 0.3:
            self.person_confidence_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #ffcc00;")
        else:
            self.person_confidence_value.setStyleSheet("font-size: 16px; font-weight: bold; color: #ff0000;")
    
    def update_status(self, status):
        self.status_label.setText(f"状态: {status}")
    
    def toggle_manual_recording(self):
        if self.glove_detector.manual_recording:
            if self.glove_detector.stop_manual_recording():
                self.manual_record_btn.setText("开始手动录制")
                self.manual_record_btn.setStyleSheet(self.get_button_style("#FF5722", "#E64A19"))
        else:
            if not self.udp_receiver or not self.udp_receiver.isRunning():
                self.status_bar.showMessage("请先启动UDP接收")
                return
                
            if self.glove_detector.start_manual_recording():
                self.manual_record_btn.setText("停止手动录制")
                self.manual_record_btn.setStyleSheet(self.get_button_style("#f44336", "#d32f2f"))

    def update_detection_interval(self, value):
        self.glove_detector.detection_interval = value
        self.detection_interval_value.setText(f"{value}ms")
        self.add_message("检测设置", f"检测间隔设置为: {value}ms")
    
    def update_hsv_range(self):
        self.glove_detector.h_min = self.h_min_slider.value()
        self.glove_detector.h_max = self.h_max_slider.value()
        self.glove_detector.s_min = self.s_min_slider.value()
        self.glove_detector.s_max = self.s_max_slider.value()
        self.glove_detector.v_min = self.v_min_slider.value()
        self.glove_detector.v_max = self.v_max_slider.value()
        
        self.h_min_value.setText(str(self.glove_detector.h_min))
        self.h_max_value.setText(str(self.glove_detector.h_max))
        self.s_min_value.setText(str(self.glove_detector.s_min))
        self.s_max_value.setText(str(self.glove_detector.s_max))
        self.v_min_value.setText(str(self.glove_detector.v_min))
        self.v_max_value.setText(str(self.glove_detector.v_max))
        
        self.add_message("HSV设置", f"H:[{self.glove_detector.h_min}-{self.glove_detector.h_max}], S:[{self.glove_detector.s_min}-{self.glove_detector.s_max}], V:[{self.glove_detector.v_min}-{self.glove_detector.v_max}]")
        
        # 在调试模式下强制更新显示
        if self.debug_mode and self.paused_frame is not None:
            self.update_debug_display()
    
    def update_color_tolerance(self, value):
        self.glove_detector.color_tolerance = value
        self.tolerance_value.setText(str(value))
        self.add_message("颜色容差", f"颜色容差设置为: {value}")
        
        # 在调试模式下强制更新显示
        if self.debug_mode and self.paused_frame is not None:
            self.update_debug_display()
    
    def update_point_threshold(self, value):
        self.glove_detector.point_threshold = value
        self.point_threshold_value.setText(str(value))
        self.add_message("点数量阈值", f"白手套点数量阈值设置为: {value}")
    
    def update_ratio_threshold(self, value):
        ratio_value = value / 100.0
        self.glove_detector.ratio_threshold = ratio_value
        self.ratio_threshold_value.setText(f"{ratio_value:.2f}")
        self.add_message("比例阈值", f"白手套点比例阈值设置为: {ratio_value:.2f}")
    
    def update_confidence_threshold(self, value):
        confidence_value = value / 100.0
        self.glove_detector.hand_confidence_threshold = confidence_value
        self.confidence_value.setText(f"{confidence_value:.2f}")
        self.add_message("置信度阈值", f"手部置信度阈值设置为: {confidence_value:.2f}")
    
    def toggle_pause_recognition(self, state):
        self.glove_detector.pause_recognition = state == Qt.Checked
        self.add_message("识别状态", f"手部识别 {'已暂停' if state == Qt.Checked else '已启用'}")
    
    def update_global_point_interval(self, value):
        self.glove_detector.global_point_interval = value
        self.global_point_interval_value.setText(str(value))
        self.add_message("点采样设置", f"全局点采样间隔设置为: {value}")

    def update_debug_display(self):
        """在调试模式下更新显示"""
        if self.paused_frame is None:
            return
            
        # 创建调试帧副本
        debug_frame = self.paused_frame.copy()
        
        # 绘制全局白手套点
        self.glove_detector.draw_global_glove_points(debug_frame)
        
        # 显示在UI上
        self.display_frame(debug_frame, self.raw_video_label)
        self.display_frame(debug_frame, self.last_detection_frame)
    
    def add_message(self, title, content):
        timestamp = datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3]
        message = f"[{timestamp}] {title}: {content}"
        self.message_box.append(message)
        self.message_box.verticalScrollBar().setValue(self.message_box.verticalScrollBar().maximum())
    
    def update_alarm(self):
        if self.glove_detector.alarm_active and self.alarm_checkbox.isChecked():
            elapsed = (datetime.datetime.now() - self.glove_detector.alarm_start_time).total_seconds()
            phase = (elapsed % (self.glove_detector.alarm_duration * 2)) / (self.glove_detector.alarm_duration * 2)
            
            if phase < 0.5:
                self.video_frame.setStyleSheet("background-color: #ff0000; border: 2px solid #ff0000;")
            else:
                self.video_frame.setStyleSheet("background-color: #1e1e1e; border: 1px solid #444;")
        else:
            self.video_frame.setStyleSheet("background-color: #1e1e1e; border: 1px solid #444;")
            
            if self.glove_detector.alarm_active:
                elapsed = (datetime.datetime.now() - self.glove_detector.alarm_start_time).total_seconds()
                if elapsed > self.glove_detector.alarm_duration * 3:
                    self.glove_detector.alarm_active = False
        if self.glove_detector.alarm_active:
            elapsed = (datetime.datetime.now() - self.glove_detector.alarm_start_time).total_seconds()
            if elapsed > self.max_alarm_duration:
                self.glove_detector.alarm_active = False
                self.glove_detector.stop_alarm_sound()
    
    def save_glove_parameters(self):
        try:
            # 更新参数
            self.glove_detector.params.update({
                "detection_interval": self.glove_detector.detection_interval,
                "pause_recognition": self.glove_detector.pause_recognition,
                "hand_confidence_threshold": self.glove_detector.hand_confidence_threshold,
                "max_missed_detections": int(self.max_misses_input.text()),
                "global_point_interval": self.glove_detector.global_point_interval,
                "h_min": self.glove_detector.h_min,
                "h_max": self.glove_detector.h_max,
                "s_min": self.glove_detector.s_min,
                "s_max": self.glove_detector.s_max,
                "v_min": self.glove_detector.v_min,
                "v_max": self.glove_detector.v_max,
                "color_tolerance": self.glove_detector.color_tolerance,
                "point_threshold": self.glove_detector.point_threshold,
                "ratio_threshold": self.glove_detector.ratio_threshold,
                "wrist_radius": self.glove_detector.wrist_radius
            })
            
            # 保存到文件
            if self.glove_detector.save_parameters():
                self.glove_detector.max_missed_detections = int(self.max_misses_input.text())
                self.add_message("参数设置", "参数已成功保存")
            else:
                self.add_message("参数错误", "保存参数失败")
        except Exception as e:
            self.add_message("参数错误", f"保存参数失败: {str(e)}")
                    
    def closeEvent(self, event):
        # 停止所有线程
        if self.detection_thread and self.detection_thread.isRunning():
            self.detection_thread.stop()
            self.detection_thread.wait(2000)
        
        # 停止录制
        self.glove_detector.stop_manual_recording()
        
        # 停止UDP接收线程
        if self.udp_receiver and self.udp_receiver.isRunning():
            self.udp_receiver.stop()
            self.udp_receiver.wait(2000)
        
        if self.udp_video_processor and self.udp_video_processor.isRunning():
            self.udp_video_processor.stop()
            self.udp_video_processor.wait(2000)
        
        super().closeEvent(event)
    
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())